# 版本: 1.0.0
# 作者: 陈振玺
# 功能: 配置管理模块，支持邮件和LLM配置的加密存储

import os
import toml
from pathlib import Path
from typing import Dict, Any, Optional
from cryptography.fernet import Fernet
from pydantic import BaseModel, validator
import base64


class EmailConfig(BaseModel):
    """邮件配置模型"""
    smtp_server: str
    smtp_port: int = 587
    username: str
    password: str = ""
    use_tls: bool = True
    use_ssl: bool = False
    
    @validator('smtp_port')
    def validate_port(cls, v):
        if not 1 <= v <= 65535:
            raise ValueError('端口号必须在1-65535之间')
        return v


class LLMConfig(BaseModel):
    """大语言模型配置模型"""
    api_url: str = "https://api.openai.com/v1"
    api_key: str = ""
    model: str = "gpt-3.5-turbo"
    max_tokens: int = 1000
    temperature: float = 0.7
    
    @validator('temperature')
    def validate_temperature(cls, v):
        if not 0.0 <= v <= 2.0:
            raise ValueError('temperature必须在0.0-2.0之间')
        return v
    
    @validator('max_tokens')
    def validate_max_tokens(cls, v):
        if v <= 0:
            raise ValueError('max_tokens必须大于0')
        return v


class ConfigManager:
    """配置管理器"""
    
    def __init__(self, config_dir: Optional[str] = None):
        """初始化配置管理器
        
        Args:
            config_dir: 配置目录路径，默认为 ~/.smart_email
        """
        if config_dir is None:
            config_dir = os.path.expanduser("~/.smart_email")
        
        self.config_dir = Path(config_dir)
        self.config_file = self.config_dir / "config.toml"
        self.key_file = self.config_dir / ".key"
        
        # 确保配置目录存在
        self.config_dir.mkdir(parents=True, exist_ok=True)
        
        # 初始化加密密钥
        self._init_encryption_key()
        
        # 加载配置
        self._config = self._load_config()
    
    def _init_encryption_key(self) -> None:
        """初始化加密密钥"""
        if not self.key_file.exists():
            # 生成新的加密密钥
            key = Fernet.generate_key()
            with open(self.key_file, 'wb') as f:
                f.write(key)
            # 设置文件权限为仅用户可读写
            os.chmod(self.key_file, 0o600)
        
        with open(self.key_file, 'rb') as f:
            self._encryption_key = f.read()
        
        self._cipher = Fernet(self._encryption_key)
    
    def _encrypt_value(self, value: str) -> str:
        """加密字符串值"""
        if not value:
            return ""
        encrypted = self._cipher.encrypt(value.encode())
        return base64.b64encode(encrypted).decode()
    
    def _decrypt_value(self, encrypted_value: str) -> str:
        """解密字符串值"""
        if not encrypted_value:
            return ""
        try:
            encrypted_bytes = base64.b64decode(encrypted_value.encode())
            decrypted = self._cipher.decrypt(encrypted_bytes)
            return decrypted.decode()
        except Exception:
            # 如果解密失败，可能是未加密的旧配置
            return encrypted_value
    
    def _load_config(self) -> Dict[str, Any]:
        """加载配置文件"""
        if not self.config_file.exists():
            return {"email": {}, "llm": {}}
        
        try:
            with open(self.config_file, 'r', encoding='utf-8') as f:
                config = toml.load(f)
            
            # 解密敏感信息
            if 'email' in config and 'password' in config['email']:
                config['email']['password'] = self._decrypt_value(config['email']['password'])
            
            if 'llm' in config and 'api_key' in config['llm']:
                config['llm']['api_key'] = self._decrypt_value(config['llm']['api_key'])
            
            return config
        except Exception as e:
            raise ValueError(f"配置文件格式错误: {e}")
    
    def _save_config(self) -> None:
        """保存配置文件"""
        # 创建配置副本用于保存
        config_to_save = self._config.copy()
        
        # 加密敏感信息
        if 'email' in config_to_save and 'password' in config_to_save['email']:
            config_to_save['email']['password'] = self._encrypt_value(
                config_to_save['email']['password']
            )
        
        if 'llm' in config_to_save and 'api_key' in config_to_save['llm']:
            config_to_save['llm']['api_key'] = self._encrypt_value(
                config_to_save['llm']['api_key']
            )
        
        with open(self.config_file, 'w', encoding='utf-8') as f:
            toml.dump(config_to_save, f)
        
        # 设置文件权限为仅用户可读写
        os.chmod(self.config_file, 0o600)
    
    def get_email_config(self) -> Optional[EmailConfig]:
        """获取邮件配置"""
        email_config = self._config.get('email', {})
        if not email_config or not email_config.get('smtp_server'):
            return None
        
        try:
            return EmailConfig(**email_config)
        except Exception as e:
            raise ValueError(f"邮件配置格式错误: {e}")
    
    def set_email_config(self, config: EmailConfig) -> None:
        """设置邮件配置"""
        self._config['email'] = config.dict()
        self._save_config()
    
    def get_llm_config(self) -> Optional[LLMConfig]:
        """获取LLM配置"""
        llm_config = self._config.get('llm', {})
        if not llm_config or not llm_config.get('api_key'):
            return None
        
        try:
            return LLMConfig(**llm_config)
        except Exception as e:
            raise ValueError(f"LLM配置格式错误: {e}")
    
    def set_llm_config(self, config: LLMConfig) -> None:
        """设置LLM配置"""
        self._config['llm'] = config.dict()
        self._save_config()
    
    def update_email_config(self, **kwargs) -> None:
        """更新邮件配置"""
        if 'email' not in self._config:
            self._config['email'] = {}
        
        self._config['email'].update(kwargs)
        
        # 验证配置
        try:
            EmailConfig(**self._config['email'])
        except Exception as e:
            raise ValueError(f"邮件配置更新失败: {e}")
        
        self._save_config()
    
    def update_llm_config(self, **kwargs) -> None:
        """更新LLM配置"""
        if 'llm' not in self._config:
            self._config['llm'] = {}
        
        self._config['llm'].update(kwargs)
        
        # 验证配置
        try:
            LLMConfig(**self._config['llm'])
        except Exception as e:
            raise ValueError(f"LLM配置更新失败: {e}")
        
        self._save_config()
    
    def get_config_dict(self) -> Dict[str, Any]:
        """获取完整配置字典（不包含敏感信息）"""
        config_copy = self._config.copy()
        
        # 隐藏敏感信息
        if 'email' in config_copy and 'password' in config_copy['email']:
            config_copy['email']['password'] = '***'
        
        if 'llm' in config_copy and 'api_key' in config_copy['llm']:
            config_copy['llm']['api_key'] = '***'
        
        return config_copy
    
    def clear_config(self) -> None:
        """清除所有配置"""
        self._config = {"email": {}, "llm": {}}
        if self.config_file.exists():
            self.config_file.unlink()
    
    def config_exists(self) -> bool:
        """检查配置文件是否存在"""
        return self.config_file.exists()
    
    def is_email_configured(self) -> bool:
        """检查邮件是否已配置"""
        email_config = self._config.get('email', {})
        required_fields = ['smtp_server', 'username', 'password']
        return all(email_config.get(field) for field in required_fields)
    
    def is_llm_configured(self) -> bool:
        """检查LLM是否已配置"""
        llm_config = self._config.get('llm', {})
        return bool(llm_config.get('api_key'))